package com.niudong.esdemo.lucene;

import org.apache.lucene.util.BytesRef;

import java.util.Arrays;

public class ConvertIntToByteRef {
public static BytesRef dedupAndEncode(int[] ordinals) {
    // 对 ordinal[]数组排序，目的是为了去重跟计算差值
    Arrays.sort(ordinals, 0, ordinals.length);
    // 先给每一个int类型分配5个字节大小的空间, 每个字节中只有7位是有效字节(描述数值),最高位是个定界符, 所以一个int类型最多要5个字节
    byte[] bytes = new byte[5*ordinals.length];
    // 记录上次处理的值，用于去重判断
    int lastOrd = -1;
    int upto = 0;
    // 遍历处理每一个int数值
    for(int i=0;i<ordinals.length;i++) {
      int ord = ordinals[i];
      // ord could be == lastOrd, so we must dedup:
      // 去重操作，当前处理的数值跟上一个如果一样的话，skip
      if (ord > lastOrd) {
        // 存储差值的变量
        int delta;
        if (lastOrd == -1) {
          // 处理第一个值, 只能储存原始的数值
          delta = ord;
        } else {
          // 处理非第一个值，就可以储存这个值与前一个值的差值
          delta = ord - lastOrd;
        }
        // if语句为真说明delta是 0~(2^7 - 1)内的值, 需要1个字节存储
        if ((delta & ~0x7F) == 0) {
          // 注意的是第8位是0(位数从0计数), 一个byte的最高位如果是0，表示是数值的最后一个byte
          bytes[upto] = (byte) delta;
          upto++;
          // if语句为真说明delta是 2^7 ~ (2^14 - 1)内的值, 需要2个字节存储
        } else if ((delta & ~0x3FFF) == 0) {
          // 这个字节的最高位是1, 表示下一个byte字节和当前字节属于同一个int类型的一部分
          bytes[upto] = (byte) (0x80 | ((delta & 0x3F80) >> 7));
          // 这个字节的最高位是0, 表示表示是数值的最后一个byte
          bytes[upto + 1] = (byte) (delta & 0x7F);
          upto += 2;
          // if语句为真说明delta是 2^14 ~ (2^21 - 1)内的值, 需要3个字节存储
        } else if ((delta & ~0x1FFFFF) == 0) {
          // 这个字节的最高位是1, 表示下一个byte字节和当前字节属于同一个int类型的一部分
          bytes[upto] = (byte) (0x80 | ((delta & 0x1FC000) >> 14));
          // 这个字节的最高位是1, 表示下一个byte字节和当前字节属于同一个int类型的一部分
          bytes[upto + 1] = (byte) (0x80 | ((delta & 0x3F80) >> 7));
          // 这个字节的最高位是0, 表示表示是数值的最后一个byte
          bytes[upto + 2] = (byte) (delta & 0x7F);
          upto += 3;
          // if语句为真说明delta是 2^21 ~ (2^28 - 1)内的值, 需要4个字节存储
        } else if ((delta & ~0xFFFFFFF) == 0) {
          bytes[upto] = (byte) (0x80 | ((delta & 0xFE00000) >> 21));
          bytes[upto + 1] = (byte) (0x80 | ((delta & 0x1FC000) >> 14));
          bytes[upto + 2] = (byte) (0x80 | ((delta & 0x3F80) >> 7));
          bytes[upto + 3] = (byte) (delta & 0x7F);
          upto += 4;
          // delta是 2^28 ~ *内的值, 需要5个字节存储
        } else {
          bytes[upto] = (byte) (0x80 | ((delta & 0xF0000000) >> 28));
          bytes[upto + 1] = (byte) (0x80 | ((delta & 0xFE00000) >> 21));
          bytes[upto + 2] = (byte) (0x80 | ((delta & 0x1FC000) >> 14));
          bytes[upto + 3] = (byte) (0x80 | ((delta & 0x3F80) >> 7));
          bytes[upto + 4] = (byte) (delta & 0x7F);
          upto += 5;
        }
        // 这里将ord保存下来是为了去重
        lastOrd = ord;
      }
    }
    return new BytesRef(bytes, 0, upto);
  }
  public static void main(String[] args) {
    int[] array = {3, 2, 2, 8, 12};
    BytesRef ref = ConvertIntToByteRef.dedupAndEncode(array);
    System.out.println(ref.toString());
  }
}